﻿using System;
using System.Configuration;
using System.Collections.Generic;
using System.Net;
using System.IO;
using System.Text;

namespace AKMII.DMRA.DataLoader
{
    /// <summary>
    /// Utility class for ftp file related operations
    /// </summary>
    class FtpUtils
    {
        /// <summary>
        /// Private constructor to disable external instance construction
        /// </summary>
        private FtpUtils()
        {
        }

        /// <summary>
        /// Download the file specified by the input source connection string
        /// to the file specified by the input destination path string
        /// </summary>
        /// <param name="srcUrl">source URL</param>
        /// <param name="userName">login user name</param>
        /// <param name="password">login user password</param>
        /// <param name="dstPath">destination path</param>
        public static void Download(string srcUrl, string userName, string password, string dstPath)
        {
            string msg = string.Format("Downloading {0} to {1}", srcUrl, dstPath);
            Console.WriteLine(msg);

            BinaryWriter writer = null;
            FtpWebResponse resp = null;
            Stream responseStream = null;
            try
            {
                FtpWebRequest request = (FtpWebRequest)WebRequest.Create(srcUrl);
                request.KeepAlive = false;
                request.Proxy = WebRequest.DefaultWebProxy;
                request.Method = WebRequestMethods.Ftp.DownloadFile;
                request.Credentials = new NetworkCredential(userName, password);
                resp = (FtpWebResponse)request.GetResponse();
                responseStream = resp.GetResponseStream();
                FileStream fs = new FileStream(dstPath, FileMode.Create);
                using (BinaryReader reader = new BinaryReader(responseStream))
                {
                    long total = 0;
                    const Int32 bufSize = 256000;
                    byte[] buf = new byte[bufSize];
                    writer = new BinaryWriter(fs);
                    for (int count = reader.Read(buf, 0, bufSize); count > 0;
                        count = reader.Read(buf, 0, bufSize))
                    {
                        writer.Write(buf, 0, count);
                        writer.Flush();
                        total += count;
                    }
                    Console.WriteLine("Total {1} bytes downloaded, status {0}",
                        resp.StatusDescription, total);
                }
            }
            catch (Exception ex)
            {
                Logger.Error("Exception occurs in download files from FTP", ex);
            }
            finally
            {
                if (resp != null)
                {
                    resp.Close();
                    resp = null;
                }
                if (responseStream != null)
                {
                    responseStream.Close();
                    responseStream = null;
                }
                if (writer != null)
                {
                    writer.Close();
                    writer = null;
                }
            }
        }

        /// <summary>
        /// Function to capture timestamp associated the file identified by the URL
        /// </summary>
        /// <param name="srcUrl">URL string</param>
        /// <param name="userName">user name</param>
        /// <param name="password">password</param>
        /// <returns>Captured timestamp</returns>
        public static DateTime GetTimestamp(string srcUrl, string userName, string password)
        {
            Uri uri = new Uri(srcUrl);

            if (uri.Scheme != Uri.UriSchemeFtp)
            {
                throw new WebException("Invalid URL: " + srcUrl);
            }

            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(srcUrl);
            request.Method = WebRequestMethods.Ftp.GetDateTimestamp;
            request.Proxy = GetWebProxy();
            request.Credentials = new NetworkCredential(userName, password);
            FtpWebResponse response = (FtpWebResponse)request.GetResponse();
            Logger.Info(string.Format("{0}{1}", srcUrl, response.LastModified)); 
            DateTime lastModified = response.LastModified;
            response.Close();

            return lastModified;
        }

        /// <summary>
        /// Upload a local file to the given ftp server site
        /// </summary>
        /// <param name="dstUrl">the input destination URL string</param>
        /// <param name="user">the login user name</param>
        /// <param name="pass">the login password</param>
        /// <param name="contents">the content to be uploaded</param>
        public static void Upload(
            string dstUrl, string userName, string password, byte[] contents)
        {
            Stream requestStream = null;
            FtpWebResponse response = null;
            try
            {
                FtpWebRequest request = (FtpWebRequest)WebRequest.Create(dstUrl);
                request.Method = WebRequestMethods.Ftp.UploadFile;
                request.Credentials = new NetworkCredential(userName, password);
                request.Proxy = null;
                request.ContentLength = contents.Length;
                requestStream = request.GetRequestStream();
                requestStream.Write(contents, 0, contents.Length);
                requestStream.Close();
                requestStream = null;
                response = (FtpWebResponse)request.GetResponse();
                Console.WriteLine("Upload File Complete, status {0}", response.StatusDescription);
                response.Close();
                response = null;
            }
            finally
            {
                if (requestStream != null)
                {
                    requestStream.Close();
                    requestStream = null;
                }
                if (response != null)
                {
                    response.Close();
                    response = null;
                }
            }
        }

        /// <summary>
        /// Upload a file specified by the input source path string to
        /// the file specified by the input destination connection string
        /// </summary>
        /// <param name="srcPath">source path string</param>
        /// <param name="dstUrl">destination URL string</param>
        /// <param name="userName">ftp login user name</param>
        /// <param name="password">ftp login user password</param>
        public static void Upload(string srcPath, string dstUrl, string userName, string password, bool isPassive)
        {
            FileStream stream = null;
            Stream requestStream = null;
            FtpWebResponse response = null;
            try
            {
                const int size = 2048;
                byte[] buffer = new byte[size];

                FtpWebRequest request = (FtpWebRequest)WebRequest.Create(dstUrl);
                request.Method = WebRequestMethods.Ftp.UploadFile;
                request.Credentials = new NetworkCredential(userName, password);
                request.Proxy = null;
                request.UsePassive = isPassive;
                requestStream = request.GetRequestStream();

                int count = 0;
                stream = File.OpenRead(srcPath);
                for (int n = stream.Read(buffer, 0, size); n > 0; n = stream.Read(buffer, 0, size))
                {
                    requestStream.Write(buffer, 0, n);
                    count += n;
                }
                stream.Close();
                requestStream.Close();
                stream = null;
                requestStream = null;
                Console.WriteLine("{0} bytes were uploaded to {1}.", count, dstUrl);

                response = (FtpWebResponse)request.GetResponse();
                Console.WriteLine("Upload File Complete, status {0}", response.StatusDescription);
                response.Close();
                response = null;
            }
            catch (Exception e)
            {
                Logger.Info(e.Message);
                if (stream != null)
                {
                    stream.Close();
                    stream = null;
                }
                if (requestStream != null)
                {
                    requestStream.Close();
                    requestStream = null;
                }
                if (response != null)
                {
                    response.Close();
                    response = null;
                }
                throw;
            }
        }

        /// <summary>
        /// Delete a file specified by the input destination connection string from the FTP server
        /// </summary>
        /// <param name="dstUrl">the input destination URL string</param>
        /// <param name="userName">the login user name</param>
        /// <param name="password">the login password</param>
        /// <returns>true if deletion successful, false otherwise</returns>
        public static bool Delete(string dstUrl, string userName, string password)
        {
            bool success = false;
            FtpWebResponse response = null;
            try
            {
                Uri serverUri = new Uri(dstUrl);
                if (serverUri.Scheme == Uri.UriSchemeFtp)
                {
                    FtpWebRequest request = (FtpWebRequest)WebRequest.Create(serverUri);
                    request.Method = WebRequestMethods.Ftp.DeleteFile;
                    request.Credentials = new NetworkCredential(userName, password);
                    request.Proxy = null;
                    response = (FtpWebResponse)request.GetResponse();
                    Console.WriteLine("Delete status: {0}", response.StatusDescription);
                    success = true;
                }
            }
            finally
            {
                if (response != null)
                {
                    response.Close();
                    response = null;
                }
            }
            return success;
        }

        /// <summary>
        /// List all files in the specified FTP URL and return all file names contained
        /// </summary>
        /// <param name="url">the input FTP URL</param>
        /// <param name="userName">the login user name</param>
        /// <param name="password">the login password</param>
        /// <returns>the list of all file names contained</returns>
        public static List<string> ListDirectory(string url, string userName, string password)
        {
            List<string> list = new List<string>();
            WebResponse response = null;
            StreamReader reader = null;
            try
            {
                FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);
                request.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
                request.Credentials = new NetworkCredential(userName, password);
                request.Proxy = null;
                response = request.GetResponse();
                reader = new StreamReader(response.GetResponseStream());
                for (string line = reader.ReadLine(); line != null; line = reader.ReadLine())
                {
                    if (!line.StartsWith("d", true, null) && !line.Contains("<DIR>"))
                    {
                        line = line.Substring(line.LastIndexOf(" ") + 1);
                        list.Add(line);
                    }
                }
            }
            finally
            {
                if (reader != null) { reader.Close(); }
                if (response != null) { response.Close(); }
            }
            return list;
        }

        internal static string GetFtpURI(string ftpDir, string file)
        {
            string url = ftpDir;
            if (!string.IsNullOrEmpty(file))
            {
                if (string.IsNullOrEmpty(url))
                {
                    url = file;
                }
                else
                {
                    if (url.EndsWith(Constants.SLASH))
                    {
                        url += file;
                    }
                    else
                    {
                        url += Constants.SLASH + file;
                    }
                }
            }
            return url;
        }

        private static WebProxy GetWebProxy()
        {
            WebProxy webProxy = null;
            string strWebProxy = string.Empty;
            if (!String.IsNullOrEmpty(strWebProxy))
            {
                webProxy = new WebProxy(strWebProxy);
            }
            return webProxy;
        }

        public static Int32 Download(string location, string fileName, FileMode fileMode, string userName, string password)
        {
            Stream receiveStream = null;
            BinaryReader readStream = null;
            HttpWebResponse myHttpWebResponse = null;
            FileStream output = null;

            BinaryWriter writer = null;
            Logger.Info(string.Format("downloading {0} to {1}", location, fileName));

            Int32 totalBytes = 0;
            try
            {
                // Creates an HttpWebRequest with the specified URL. 
                HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(new Uri(location));
                myHttpWebRequest.UserAgent = "Utilities.Download";

                // Creates user defined credential
                myHttpWebRequest.Credentials = new NetworkCredential(userName, password);

                // Sends the HttpWebRequest and waits for the response.            
                myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();

                // Gets the stream associated with the response.
                receiveStream = myHttpWebResponse.GetResponseStream();

                // Pipes the stream to a higher level stream reader with the required encoding format. 
                using (readStream = new BinaryReader(receiveStream))
                {
                    const Int32 bufSize = 256000;
                    byte[] read = new byte[bufSize];
                    int count = readStream.Read(read, 0, bufSize);
                    output = new FileStream(fileName, fileMode);
                    writer = new BinaryWriter(output);

                    while (count > 0)
                    {
                        totalBytes += count;
                        writer.Write(read, 0, count);
                        count = readStream.Read(read, 0, bufSize);
                    }
                    Logger.Info(string.Format("downloaded {2} bytes from {0} to {1}"
                      , location
                      , fileName
                      , totalBytes.ToString()));
                }
            }
            finally
            {
                if (myHttpWebResponse != null)
                {
                    myHttpWebResponse.Close();
                    myHttpWebResponse = null;
                }
                if (receiveStream != null)
                {
                    receiveStream.Close();
                    receiveStream = null;
                }
                if (output != null)
                {
                    output.Flush();
                    output.Close();
                    output = null;
                }
            }
            return totalBytes;
        }
    }
}
